﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Drawing.Drawing2D;
using System.Reflection;
using ArtMath.Core.Data;
using ArtMath.Attribute;
using ArtMath.CommonType;
using ArtMath.Setting;
using ArtMath.Data;
using ArtMath.Parameter;
using ArtMath.PointCollection;
using ArtMath.Component.Point;
using ArtMath.Core.Helper;
using ArtMath.Component.Line;
using ArtMath.Resource;
using ArtMath.Component.Manager;
using ArtMath.Component.Annotation;
using ArtMath.Flag;

namespace ArtMath.Component
{
    public enum PointsRelocationReason
    {
        CoordinateRotated,
        CoordinateOriginPointTranslated,
        XAxisScaled,
        YAxisScaled,
        PolarAxisScaled,
        UnitChanged,
        AngleUnitChanged,
    }
    [Serializable]
    public abstract class ShapeBase : Base, IStringSettable
    {
        public bool Track = Settings.Instance.Track, ShowTrack = Settings.Instance.ShowTrack;
        ColorX trackColor;
        PadStringX trackWidth;
        public int OrderIndex;
        [Browsable(false)]
        public List<LineBase> InHosts { get; } = new List<LineBase>();
        [Browsable(false)]
        public List<CalculatedParameter> CalculatedParameters { get; } = new List<CalculatedParameter>();
        public ShapeBase(SketchPad pad, string name, ToolTypeName toolType, bool addToList = true) : base(pad, name, toolType, addToList)
        {
            trackColor = new ColorX(Pad, Settings.Instance.TrackColor);
            TrackWidth = new PadStringX(Pad, Settings.Instance.TrackWidth);
        }
        [Browsable(false)]
        protected Pen TrackPen
        {
            get
            {
                return new Pen(trackColor.Color, TrackWidth.FloatValue) { LineJoin = LineJoin.Round };
            }
        }
        [MultilingualCategory("Track"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "False"), MultilingualDisplayName("RecordTrack")]
        public virtual bool Track_
        {
            get { return Track; }
            set { Track = value; }
        }
        [MultilingualCategory("Track"), MultilingualDisplayName("TrackColor")]
        public Color TrackColor
        {
            get { return trackColor.Color; }
            set { trackColor.Color = value; }
        }
        [MultilingualCategory("Track"), MultilingualDisplayName("TrackWidth")]
        public PadStringX TrackWidth
        {
            get { return trackWidth; }
            set { trackWidth = value; }
        }
        [MultilingualCategory("Track"), TypeConverter(typeof(ArtMath.Converter.BooleanConverter)), DefaultValue(typeof(bool), "True"), MultilingualDisplayName(null, "Show", "Track")]
        public bool ShowTrack_
        {
            get { return ShowTrack; }
            set
            {
                ShowTrack = value;
                InChildrenIterator((ShapeBase obj) =>
                {
                    obj.ShowTrack_ = ShowTrack_;
                    return true;
                });
                Pad.Canvas.Invalidate();
            }
        }
        public override bool IsIndependent
        {
            get
            {
                return base.IsIndependent && IsTop;
            }
        }
        /// <summary>
        /// 获取组件所在组件管理器
        /// </summary>
        [Browsable(false)]
        public virtual ComponentManagerBase ComponentManager
        {
            get { return null; }
        }
        [Browsable(false)]
        protected virtual bool DoAngleFix
        {
            get { return true; }
        }
        public override bool IsTop
        {
            get { return InHosts.Count((LineBase line) => { return line.ObjectState == ObjectState.Normal; }) == 0; }
        }
        public override bool Visible
        {
            get
            {
                return base.Visible;
            }

            set
            {
                base.Visible = value;
                InChildrenIterator((ShapeBase obj) =>
                {
                    if (value)
                    {
                        obj.Visible = true;
                    }
                    else
                    {
                        //只要该元件存在显示的内父元件，就不隐藏
                        bool set = true;
                        obj.AllInHostsIterator((LineBase line) =>
                        {
                            if (line.Visible)
                            {
                                set = false;
                            }
                            return set;
                        });
                        if (set) obj.Visible = false;
                    }
                    return true;
                });
            }
        }
        public override bool Selected_
        {
            get
            {
                if (IsEntity && !Selected) return false;
                bool res = true;
                InChildrenIterator((ShapeBase obj) =>
                {
                    if (!obj.Selected_)
                    {
                        res = false;
                        return false;
                    }
                    return true;
                });
                return res;
            }

            set
            {
                Selected = value;
                InChildrenIterator((ShapeBase obj) => 
                {
                    obj.Selected_ = Selected;
                    return true;
                });
            }
        }
        [Browsable(false)]
        public virtual bool Locked
        {
            get { return false; }
            set
            {
                if (LayerItem != null)
                {
                    LayerItem.Locked = value;
                }
            }
        }
        public override void SetInPicker(bool value)
        {
            base.SetInPicker(value);
            //bool flag = true;
            AllInHostsIterator((LineBase line) =>
            {
                if (!line.IsEntity)
                {
                    if (line.LayerItem != null)
                    {
                        line.LayerItem.Selected = line.Selected_;
                    }
                }
                return true;
            });
            if (LayerItem != null)
            {
                LayerItem.Selected = Selected_;
            }
        }
        [Browsable(false)]
        public abstract PointCollectionsManager PSkeleton { get; }
        [Browsable(false)]
        public abstract PointCollectionsManager Skeleton { get; }
        [Browsable(false)]
        public abstract PointCollectionsManager NewSkeleton { get; }
        [Browsable(false)]
        public abstract PointCollectionsManager NewPSkeleton { get; }
        public override StyleState StyleState_
        {
            get
            {
                return base.StyleState_;
            }

            set
            {
                base.StyleState_ = value;
                InChildrenIterator((ShapeBase obj) => 
                {
                    obj.StyleState_ = StyleState_;
                    return true;
                });
            }
        }
        public override void Delete(bool recoverable = false)
        {
            base.Delete(recoverable);
            foreach (CalculatedParameter item in CalculatedParameters)
            {
                if (item.ObjectState == ObjectState.Normal)
                {
                    item.Delete(recoverable);
                }
            }
        }
        public override bool CanRecover
        {
            get
            {
                return base.CanRecover && InHosts.TrueForAll((LineBase line) => { return line.ObjectState == ObjectState.Normal; });
            }
        }
        //public override void Recover(object arg)
        //{
        //    foreach (CalculatedParameter item in CalculatedParameters)
        //    {
        //        item.Recover(arg);
        //    }
        //    base.Recover(arg);
        //}
        public virtual void ExternalObjectMove(PointBase movePoint, Base sender = null)
        {
            foreach (ComponentManagerBase mgr in ComponentManagers)
            {
                mgr.PointMoving_ManagedComponentsLimit(this);
            }
        }
        public override void Paint(Graphics gra)
        {
            InChildrenIterator((ShapeBase obj) =>
            {
                if (obj.LayerItem == null)
                {
                    obj.Paint(gra);
                }
                return true;
            });
        }
        public override void CheckInRegion(RectangleD rect)
        {
            InChildrenIterator((ShapeBase obj) => 
            {
                obj.CheckInRegion(rect);
                return true;
            });
        }
        public override void PaintProperties(Graphics gra)
        {
            InChildrenIterator((ShapeBase obj) =>
            {
                if (obj.LayerItem == null)
                {
                    obj.PaintProperties(gra);
                }
                return true;
            });
            base.PaintProperties(gra);
        }
        //public override void RefreshPropertyDisplayersText()
        //{
        //    InChildrenIterator((ShapeBase obj) =>
        //    {
        //        if (obj.LayerItem == null)
        //        {
        //            obj.RefreshPropertyDisplayersText();
        //        }
        //        return true;
        //    });
        //    base.RefreshPropertyDisplayersText();
        //}
        //public override void RefreshPropertyDisplayersLocation()
        //{
        //    InChildrenIterator((ShapeBase obj) =>
        //    {
        //        if (obj.LayerItem == null)
        //        {
        //            obj.RefreshPropertyDisplayersLocation();
        //        }
        //        return true;
        //    });
        //    base.RefreshPropertyDisplayersLocation();
        //}
        public override Base[] CheckNearBy(PointD Plocation)
        {
            HashSet<Base> set = new HashSet<Base>();
            InChildrenIterator((ShapeBase obj) =>
            {
                set.AddRange(obj.CheckNearBy(Plocation));
                return true;
            });
            return set.ToArray();
        }
        public override void CoordinateSystemTypeChanged()
        {
            base.CoordinateSystemTypeChanged();//base必须先执行，否则先切换点类型会导致RotateCenter刷新，而它的类型还没变成相应坐标系的类型，导致错误的新值
            ForEachPoint((PointBase pt) =>
            {
                pt.ConvertType(pt.ToolType);
                return true;
            });
        }
        public virtual void InChildrenIterator(Predicate<ShapeBase> act)
        {

        }
        /// <summary>
        /// BFS遍历它的所有内子元件
        /// </summary>
        /// <param name="act"></param>
        public void AllInChildrenIterator(Predicate<ShapeBase> act)
        {
            if (act == null) return;
            Queue<ShapeBase> Q = new Queue<ShapeBase>();
            HashSet<ShapeBase> flags = new HashSet<ShapeBase>();
            InChildrenIterator((ShapeBase item) =>
            {
                if (flags.Add(item))
                {
                    Q.Enqueue(item);
                }
                return true;
            });
            while (Q.Count > 0)
            {
                ShapeBase shape = Q.Dequeue();
                if (!act(shape))
                {
                    break;
                }
                shape.InChildrenIterator((ShapeBase item) =>
                {
                    if (flags.Add(item))
                    {
                        Q.Enqueue(item);
                    }
                    return true;
                });
            }
        }
        /// <summary>
        /// BFS遍历它的所有内父元件
        /// </summary>
        /// <param name="act">对每个内父元件进行的操作，并返回一个bool值用于指示是否继续遍历</param>
        public void AllInHostsIterator(Predicate<LineBase> act)
        {
            if (act == null) return;
            Queue<LineBase> Q = new Queue<LineBase>();
            HashSet<LineBase> flags = new HashSet<LineBase>();
            foreach (LineBase item in InHosts)
            {
                //if (item.Deleted) continue;
                if (flags.Add(item))
                {
                    Q.Enqueue(item);
                }
            }
            while (Q.Count > 0)
            {
                LineBase line = Q.Dequeue();
                if (!act(line))
                {
                    break;
                }
                foreach (LineBase item in line.InHosts)
                {
                    //if (item.Deleted) continue;
                    if (flags.Add(item))
                    {
                        Q.Enqueue(item);
                    }
                }
            }
        }
        public string GetInHostsNames(string splitter = ";")
        {
            StringBuilder sb = new StringBuilder();
            AllInHostsIterator((LineBase line) =>
            {
                sb.Append(line.Name + splitter);
                return true;
            });
            if (sb.Length == 0) return string.Empty;
            return sb.ToString(0, sb.Length - splitter.Length);
        }
        public virtual void AddTracks()
        {

        }
        public virtual void ClearTracks()
        {
            InChildrenIterator((ShapeBase obj) =>
            {
                obj.ClearTracks();
                return true;
            });
        }
        //public override HashSet<Base> GetRelatedObject()
        //{
        //    HashSet<Base> set = new HashSet<Base>(base.GetRelatedObject());
        //    if (!IsTop)
        //    {
        //        AllInHostsIterator((LineBase line) =>
        //        {
        //            set.AddRange(line.GetRelatedObject());
        //            return true;
        //        });
        //    }
        //    return set;
        //}
        protected override HashSet<Base> getRelatedObjects()
        {
            HashSet<Base> set = base.getRelatedObjects();
            set.AddRange(InHosts);
            InChildrenIterator((ShapeBase obj) =>
            {
                set.Add(obj);
                return true;
            });
            return set;
        }
        public virtual void ForEachStraightLine(Predicate<StraightLineBase> act)
        {

        }
        public virtual void ForEachPoint(Predicate<PointBase> act)
        {

        }
        [Browsable(false)]
        public virtual bool ContainsMovablePoint
        {
            get
            {
                return false;
            }
        }
        public virtual bool PrepareMove(ShapeBase sender = null)
        {
            return false;
        }
        public virtual bool CleanMove(ShapeBase sender = null)
        {
            return false;
        }
        //public override void Move(PointD location, bool isCompulsive = false, Base sender = null)
        //{
        //    foreach (CalculatedParameter item in Parameters)
        //    {
        //        item.Move(location, isCompulsive, sender);
        //    }
        //}
        public bool HasCommonInHost(ShapeBase another)
        {
            bool has = false;
            AllInHostsIterator((LineBase line) =>
            {
                line.InChildrenIterator((ShapeBase shape) =>
                {
                    if (shape == another)
                    {
                        has = true;
                    }
                    return !has;
                });
                return !has;
            });
            return has;
        }
        public bool HasInChild(ShapeBase child)
        {
            bool has = false;
            AllInChildrenIterator((ShapeBase shape) =>
            {
                if (child == shape)
                {
                    has = true;
                }
                return !has;
            });
            return has;
        }
        public void DisableTrack(Action act)
        {
            if (act == null) return;
            bool bkp = Track_;
            Track_ = false;
            act();
            Track_ = bkp;
        }
        /// <summary>
        /// 1.当部件移动时，重新计算与该元件有关的参数；2.添加轨迹
        /// </summary>
        /// <param name="component">移动的部件</param>
        public virtual void ComponentChanged(ShapeBase component)
        {
            if (Track && Visible)
            {
                AddTracks();//自底向上添加轨迹
            }
            foreach (CalculatedParameter item in CalculatedParameters)
            {
                item.Value.SetExpression(item.GetNewValue());
            }
            foreach (ObjectParameter item in ObjectParameters)
            {
                item.Value.SetExpression(item.GetNewValue());
            }
            foreach (LineBase item in InHosts)
            {
                if (item.IsCreationCompleted)
                {
                    item.ComponentChanged(this);
                }
            }
            RefreshPropertyDisplayersText();
            RefreshPropertyDisplayersLocation();
        }
        public virtual void PaintTrack(Graphics gra)
        {
            InChildrenIterator((ShapeBase obj) =>
            {
                obj.PaintTrack(gra);
                return true;
            });
        }
        public override void CreateComponents()
        {
            if (ReferenceEquals(Pad.HostObj, this)) Pad.HostObj = null;
            if (ReferenceEquals(Pad.TrackObj, this)) Pad.TrackObj = null;
            //Pad.DoCheckNearBy = true;
            ShapeBase sb = IsTop ? CheckRepeat() : null;
            if (sb == null)
            {
                base.CreateComponents();
            }
            else
            {
                UIInteraction.StatusMessage.ShowMainStatus(Resources.Translate("Ready"));
                UIInteraction.StatusMessage.ClearSecondaryStatus();
                throw new ComponentCreatingException(this, string.Format(Resources.Translate("ComponentExist"), sb.Name));
            }
        }
        protected void ShowStatus(string status)
        {
            if (ReferenceEquals(Pad.HostObj, this))
            {
                UIInteraction.StatusMessage.ShowMainStatus(status);
            }
            else
            {
                UIInteraction.StatusMessage.ShowSecondaryStatus(Resources.LeftSquareBracket + status);
            }
        }
        protected void AppendStatus(string status, bool close = false)
        {
            if (ReferenceEquals(Pad.HostObj, this))
            {
                if (close)
                {
                    status += Resources.RightParenthesis;
                }
                UIInteraction.StatusMessage.AppendMainStatus(status);
            }
            else
            {
                if (close)
                {
                    status += Resources.RightSquareBracket;
                }
                UIInteraction.StatusMessage.AppendSecondaryStatus(status);
            }
        }
        /// <summary>
        /// 检查元件是否重复
        /// </summary>
        /// <returns></returns>
        public virtual ShapeBase CheckRepeat()
        {
            return null;
        }
        public virtual void SetFromString(string exp)
        {

        }
        public override void UseFormat(Base obj)
        {
            base.UseFormat(obj);
            if (Settings.Instance.InChildrenUseFormat)
            {
                InChildrenIterator((ShapeBase sb) =>
                {
                    ShapeBase tmp = getCorrespondingInChild(sb, obj as ShapeBase);
                    if (tmp != null)
                    {
                        sb.UseFormat(tmp);
                    }
                    return true;
                });
            }
        }
        ShapeBase getCorrespondingInChild(ShapeBase inChild, ShapeBase anotherObj)
        {
            PropertyInfo objProp = null;
            foreach (PropertyInfo item in GetType().GetProperties())
            {
                if (item.GetValue(this, null) == inChild)
                {
                    objProp = item;
                    break;
                }
            }
            if (objProp == null) return null;
            foreach (PropertyInfo item in anotherObj.GetType().GetProperties())
            {
                if (item == objProp)
                {
                    return item.GetValue(anotherObj, null) as ShapeBase;
                }
            }
            return null;
        }
        protected void simulateClick(PointCreatingArgs arg)
        {
            Pad.PointAbsorbMode = arg.TryAbsorb ? PointAbsorb.Auto : PointAbsorb.No;
            PointD pxy = Pad.XYToPxy(arg.Location);
            Pad.CheckNearBy(pxy, false, true, (Base obj) =>
            {
                return !(obj is CustomBase);
            });
            Pad.HostTrackComponentBuilding(pxy);
            if (arg.Fixed)
            {
                Flags.UseCtrl(() =>
                {
                    Pad.CreateAndCommand(this);
                });
            }
            else
            {
                Pad.CreateAndCommand(this);
            }
        }
    }
}
